diff options
| author | Fuwn <[email protected]> | 2024-10-09 00:41:20 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2024-10-09 00:41:43 -0700 |
| commit | 998b63a35256ac985a5a2714dd1ca451af4dfd8a (patch) | |
| tree | 50796121a9d5ab0330fdc5d7e098bda2860d9726 /src/routes/user/[user]/badges | |
| parent | feat(graphql): add badgeCount field (diff) | |
| download | due.moe-998b63a35256ac985a5a2714dd1ca451af4dfd8a.tar.xz due.moe-998b63a35256ac985a5a2714dd1ca451af4dfd8a.zip | |
chore(prettier): use spaces instead of tabs
Diffstat (limited to 'src/routes/user/[user]/badges')
| -rw-r--r-- | src/routes/user/[user]/badges/+page.gql | 54 | ||||
| -rw-r--r-- | src/routes/user/[user]/badges/+page.svelte | 2040 |
2 files changed, 1047 insertions, 1047 deletions
diff --git a/src/routes/user/[user]/badges/+page.gql b/src/routes/user/[user]/badges/+page.gql index afdd797d..060b38e7 100644 --- a/src/routes/user/[user]/badges/+page.gql +++ b/src/routes/user/[user]/badges/+page.gql @@ -1,31 +1,31 @@ query BadgeWallUser($id: Int!) { - User(id: $id) { - id + User(id: $id) { + id - badges { - post - image - description - id - time - category - hidden - source - designer - shadow_hidden - click_count - } + badges { + post + image + description + id + time + category + hidden + source + designer + shadow_hidden + click_count + } - preferences { - created_at - updated_at - user_id - pinned_hololive_streams - hide_missing_badges - biography - badge_wall_css - hide_awc_badges - pinned_badge_wall_categories - } - } + preferences { + created_at + updated_at + user_id + pinned_hololive_streams + hide_missing_badges + biography + badge_wall_css + hide_awc_badges + pinned_badge_wall_categories + } + } } diff --git a/src/routes/user/[user]/badges/+page.svelte b/src/routes/user/[user]/badges/+page.svelte index 3857d18b..44dd852a 100644 --- a/src/routes/user/[user]/badges/+page.svelte +++ b/src/routes/user/[user]/badges/+page.svelte @@ -1,1042 +1,1042 @@ <script lang="ts"> - import AWC from './../../../../lib/User/BadgeWall/AWC.svelte'; - import { user, type User } from '$lib/Data/AniList/user'; - import type { Badge } from '../../../../graphql/$types'; - import { onDestroy, onMount } from 'svelte'; - import HeadTitle from '$lib/Home/HeadTitle.svelte'; - import { databaseTimeToDate, dateToInputTime, inputTimeToDatabaseTime } from '$lib/Utility/time'; - import proxy from '$lib/Utility/proxy'; - import locale from '$stores/locale'; - import Skeleton from '$lib/Loading/Skeleton.svelte'; - import Message from '$lib/Loading/Message.svelte'; - import Dropdown from '$lib/Layout/Dropdown.svelte'; - import { activityText } from '$lib/Data/AniList/activity'; - import SettingHint from '$lib/Settings/SettingHint.svelte'; - import Popup from '$lib/Layout/Popup.svelte'; - import { page } from '$app/stores'; - import { browser } from '$app/environment'; - import BadgePreview from '$lib/User/BadgeWall/BadgePreview.svelte'; - import authorisedJson from '$lib/Data/Static/authorised.json'; - import identity from '$stores/identity'; - import '$lib/User/BadgeWall/badges.css'; - import Badges from '$lib/User/BadgeWall/Badges.svelte'; - import type { IndexedBadge } from '$lib/User/BadgeWall/badge'; - import { graphql } from '$houdini'; - import type { Preferences } from '../../../../graphql/user/$types'; - - export let data; - - $: ({ BadgeWallUser } = data); - $: preferences = $BadgeWallUser.fetching - ? undefined - : ($BadgeWallUser.data?.User.preferences as Preferences); - - $: if (browser && preferences && preferences.badge_wall_css) { - const sanitise = (css: string) => - css - .replace(/\/\*[\s\S]*?\*\//g, '') - .replace(/<\/?[^>]+(>|$)/g, '') - .replace( - /(expression|javascript|vbscript|onerror|onload|onclick|onmouseover|onmouseout|onmouseup|onmousedown|onkeydown|onkeyup|onkeypress|onblur|onfocus|onsubmit|onreset|onselect|onchange|ondblclick):/gi, - '' - ) - .replace(/(behaviour|behavior|moz-binding|content):/gi, '') - .replace(/\s+/g, ' ') - .trim(); - const style = document.createElement('style'); - - style.dataset.badgeWall = 'true'; - style.innerHTML = sanitise(preferences.badge_wall_css); - - document.head.appendChild(style); - } - - const updateBadgeQuery = graphql(` - mutation UpdateBadge( - $id: Int - $post: String - $image: String - $description: String - $time: String - $category: String - $hidden: Boolean - $source: String - $designer: String - ) { - updateBadge( - id: $id - post: $post - image: $image - description: $description - time: $time - category: $category - hidden: $hidden - source: $source - designer: $designer - ) { - id - - badges { - post - image - description - id - time - category - hidden - source - designer - shadow_hidden - click_count - } - } - } - `); - - const pruneBadgesQuery = graphql(` - mutation PruneUserBadges { - pruneUserBadges { - id - - badges { - post - image - description - id - time - category - hidden - source - designer - shadow_hidden - click_count - } - } - } - `); - - const hideCategoryQuery = graphql(` - mutation HideCategory($category: String) { - hideBadge(category: $category) { - id - - badges { - post - image - description - id - time - category - hidden - source - designer - shadow_hidden - click_count - } - } - } - `); - - const deleteBadgeQuery = graphql(` - mutation DeleteBadge($id: Int!) { - deleteBadge(id: $id) { - id - - badges { - post - image - description - id - time - category - hidden - source - designer - shadow_hidden - click_count - } - } - } - `); - - const shadowHideBadgeQuery = graphql(` - mutation ShadowHideBadge($id: Int!, $state: Boolean) { - shadowHideBadge(id: $id, state: $state) { - id - - badges { - id - } - } - } - `); - - interface ImportImage { - link?: string; - image: string; - } - - let editMode = false; - let importMode = false; - let error: null | string; - let awcPromise: Promise<Response>; - let confirmDelete = 0; - let confirmPrune = 0; - let selectedBadge: IndexedBadge | undefined = undefined; - let loadError: string | null = null; - const isId = /^\d+$/.test(data.username); - let importImages: ImportImage[] | undefined = undefined; - let importLinks = false; - let importCategory = ''; - let importReplies = false; - let badger: Partial<User>; - let migrateMode = false; - let hideMode = false; - const authorised = authorisedJson.includes($identity.id); - let noticeDismissed = false; - - $: categoryFilter = new URLSearchParams($page.url.searchParams).get('category'); - - type GroupedBadges = { [key: string]: IndexedBadge[] }; - - const setShadowHide = () => - shadowHideBadgeQuery.mutate({ - id: badger.id as number - }); - - onMount(async () => { - if (browser && localStorage.getItem('badgeWallNoticeDismissed')) noticeDismissed = true; - - badger = isId - ? { - id: parseInt(data.username), - name: 'User' - } - : await user(data.username); - - if (!isId && !badger) { - loadError = 'User not found.'; - - return; - } - - awcPromise = fetch(proxy(`https://awc.moe/challenger/${badger.name}`)); - }); - - onDestroy(() => { - if (browser) - Array.from(document.head.querySelectorAll('style')).forEach((style) => { - if (style.dataset.badgeWall) style.remove(); - }); - }); - - const submitBadge = () => { - const imageURL = document.querySelector('input[name="image_url"]') as HTMLInputElement; - const activityURL = document.querySelector('input[name="activity_url"]') as HTMLInputElement; - const description = document.querySelector('input[name="description"]') as HTMLInputElement; - const time = document.querySelector('input[type="datetime-local"]') as HTMLInputElement; - const category = document.querySelector('input[name="category"]') as HTMLInputElement; - const hidden = document.querySelector('input[name="hidden"]') as HTMLInputElement; - const source = document.querySelector('input[name="source"]') as HTMLInputElement; - const designer = document.querySelector('input[name="designer"]') as HTMLInputElement; - - if (!imageURL.value) { - error = 'Image URL cannot be empty.'; - - return; - } - - if ( - !imageURL.value.startsWith('http') || - (activityURL.value.length > 0 && !activityURL.value.startsWith('http')) - ) { - error = 'URLs must start with http or https.'; - - return; - } - - updateBadgeQuery - .mutate({ - id: selectedBadge?.id, - image: imageURL.value, - post: activityURL.value || '#', - description: description.value, - category: category.value, - time: time.valueAsDate ? inputTimeToDatabaseTime(time.valueAsDate) : undefined, - hidden: hidden.value === 'Hidden', - source: source.value, - designer: designer.value - }) - .then(() => { - error = null; - imageURL.value = ''; - activityURL.value = ''; - description.value = ''; - category.value = ''; - hidden.value = 'Shown'; - selectedBadge = undefined; - source.value = ''; - designer.value = ''; - }); - }; - - const removeAllBadges = () => { - if (confirmPrune === 2) { - confirmPrune = 0; - } else if (confirmPrune === 0) { - confirmPrune = 1; - - return; - } else { - confirmPrune = 2; - - return; - } - - selectedBadge = undefined; - - pruneBadgesQuery.mutate(null).then(); - }; - - const removeBadge = (badge: Badge) => { - if (!badge.id) return; - - if (confirmDelete === badge.id * 2) { - confirmDelete = 0; - } else if (confirmDelete / 4 === badge.id) { - confirmDelete = badge.id * 2; - - return; - } else { - confirmDelete = badge.id * 2; - - return; - } - - selectedBadge = undefined; - - deleteBadgeQuery - .mutate({ - id: badge.id - }) - .then(); - }; - - const groupBadges = (badges: IndexedBadge[]) => { - const groupedBadges: GroupedBadges = {}; - - badges.forEach((badge) => { - if (!badge.category) badge.category = 'Uncategorised'; - - if (!groupedBadges[badge.category]) groupedBadges[badge.category] = []; - - groupedBadges[badge.category].push(badge); - }); - - Object.entries(groupedBadges).forEach(([_category, badges]) => { - badges.forEach((badge, index) => { - badge.index = index; - }); - }); - - return Object.entries(groupedBadges) - .sort((a, b) => a[1].length - b[1].length) - .sort((a, b) => { - const pinnedCategories = - preferences && preferences.pinned_badge_wall_categories - ? preferences.pinned_badge_wall_categories - : ([] as string[]); - const aIndex = pinnedCategories.indexOf(a[0]); - const bIndex = pinnedCategories.indexOf(b[0]); - - if (aIndex === -1 && bIndex === -1) return 0; - if (aIndex === -1) return 1; - if (bIndex === -1) return -1; - - return aIndex - bIndex; - }) - .reduce((set: GroupedBadges, [key, value]) => { - set[key] = value; - - return set; - }, {}); - }; - - const parsePost = async () => { - if (importImages && importImages.length > 0) importImages = undefined; - - const link = (document.querySelector('#import_activity_url') as HTMLInputElement).value; - const type = link.replace(/.*\/(activity|thread)\/(\d+).*/, '$1'); - const id = link.replace(/.*\/(activity|thread)\/(\d+).*/, '$2'); - - if (type !== 'activity') return null; - - let text = await activityText(parseInt(id), importReplies); - - const images: ImportImage[] = []; - - if (importLinks) { - Array.from(new DOMParser().parseFromString(text, 'text/html').querySelectorAll('a')).forEach( - (a) => { - const anchor = a as HTMLAnchorElement; - - if (anchor.querySelector('img')) { - images.push({ - link: anchor.href, - image: (anchor.querySelector('img') as HTMLImageElement).src - }); - } - } - ); - - text = text.replace(/<a.*?>.*?<img.*?>.*?<\/a>/g, ''); - - Array.from( - new DOMParser().parseFromString(text, 'text/html').querySelectorAll('img') - ).forEach((img) => { - const image = img as HTMLImageElement; - - images.push({ - image: image.src - }); - }); - } else { - Array.from( - new DOMParser().parseFromString(text, 'text/html').querySelectorAll('img') - ).forEach((img) => { - const image = img as HTMLImageElement; - - images.push({ - image: image.src - }); - }); - } - - importImages = images; - }; - - const importBadges = () => - fetch( - `/api/badges?import=true + import AWC from './../../../../lib/User/BadgeWall/AWC.svelte'; + import { user, type User } from '$lib/Data/AniList/user'; + import type { Badge } from '../../../../graphql/$types'; + import { onDestroy, onMount } from 'svelte'; + import HeadTitle from '$lib/Home/HeadTitle.svelte'; + import { databaseTimeToDate, dateToInputTime, inputTimeToDatabaseTime } from '$lib/Utility/time'; + import proxy from '$lib/Utility/proxy'; + import locale from '$stores/locale'; + import Skeleton from '$lib/Loading/Skeleton.svelte'; + import Message from '$lib/Loading/Message.svelte'; + import Dropdown from '$lib/Layout/Dropdown.svelte'; + import { activityText } from '$lib/Data/AniList/activity'; + import SettingHint from '$lib/Settings/SettingHint.svelte'; + import Popup from '$lib/Layout/Popup.svelte'; + import { page } from '$app/stores'; + import { browser } from '$app/environment'; + import BadgePreview from '$lib/User/BadgeWall/BadgePreview.svelte'; + import authorisedJson from '$lib/Data/Static/authorised.json'; + import identity from '$stores/identity'; + import '$lib/User/BadgeWall/badges.css'; + import Badges from '$lib/User/BadgeWall/Badges.svelte'; + import type { IndexedBadge } from '$lib/User/BadgeWall/badge'; + import { graphql } from '$houdini'; + import type { Preferences } from '../../../../graphql/user/$types'; + + export let data; + + $: ({ BadgeWallUser } = data); + $: preferences = $BadgeWallUser.fetching + ? undefined + : ($BadgeWallUser.data?.User.preferences as Preferences); + + $: if (browser && preferences && preferences.badge_wall_css) { + const sanitise = (css: string) => + css + .replace(/\/\*[\s\S]*?\*\//g, '') + .replace(/<\/?[^>]+(>|$)/g, '') + .replace( + /(expression|javascript|vbscript|onerror|onload|onclick|onmouseover|onmouseout|onmouseup|onmousedown|onkeydown|onkeyup|onkeypress|onblur|onfocus|onsubmit|onreset|onselect|onchange|ondblclick):/gi, + '' + ) + .replace(/(behaviour|behavior|moz-binding|content):/gi, '') + .replace(/\s+/g, ' ') + .trim(); + const style = document.createElement('style'); + + style.dataset.badgeWall = 'true'; + style.innerHTML = sanitise(preferences.badge_wall_css); + + document.head.appendChild(style); + } + + const updateBadgeQuery = graphql(` + mutation UpdateBadge( + $id: Int + $post: String + $image: String + $description: String + $time: String + $category: String + $hidden: Boolean + $source: String + $designer: String + ) { + updateBadge( + id: $id + post: $post + image: $image + description: $description + time: $time + category: $category + hidden: $hidden + source: $source + designer: $designer + ) { + id + + badges { + post + image + description + id + time + category + hidden + source + designer + shadow_hidden + click_count + } + } + } + `); + + const pruneBadgesQuery = graphql(` + mutation PruneUserBadges { + pruneUserBadges { + id + + badges { + post + image + description + id + time + category + hidden + source + designer + shadow_hidden + click_count + } + } + } + `); + + const hideCategoryQuery = graphql(` + mutation HideCategory($category: String) { + hideBadge(category: $category) { + id + + badges { + post + image + description + id + time + category + hidden + source + designer + shadow_hidden + click_count + } + } + } + `); + + const deleteBadgeQuery = graphql(` + mutation DeleteBadge($id: Int!) { + deleteBadge(id: $id) { + id + + badges { + post + image + description + id + time + category + hidden + source + designer + shadow_hidden + click_count + } + } + } + `); + + const shadowHideBadgeQuery = graphql(` + mutation ShadowHideBadge($id: Int!, $state: Boolean) { + shadowHideBadge(id: $id, state: $state) { + id + + badges { + id + } + } + } + `); + + interface ImportImage { + link?: string; + image: string; + } + + let editMode = false; + let importMode = false; + let error: null | string; + let awcPromise: Promise<Response>; + let confirmDelete = 0; + let confirmPrune = 0; + let selectedBadge: IndexedBadge | undefined = undefined; + let loadError: string | null = null; + const isId = /^\d+$/.test(data.username); + let importImages: ImportImage[] | undefined = undefined; + let importLinks = false; + let importCategory = ''; + let importReplies = false; + let badger: Partial<User>; + let migrateMode = false; + let hideMode = false; + const authorised = authorisedJson.includes($identity.id); + let noticeDismissed = false; + + $: categoryFilter = new URLSearchParams($page.url.searchParams).get('category'); + + type GroupedBadges = { [key: string]: IndexedBadge[] }; + + const setShadowHide = () => + shadowHideBadgeQuery.mutate({ + id: badger.id as number + }); + + onMount(async () => { + if (browser && localStorage.getItem('badgeWallNoticeDismissed')) noticeDismissed = true; + + badger = isId + ? { + id: parseInt(data.username), + name: 'User' + } + : await user(data.username); + + if (!isId && !badger) { + loadError = 'User not found.'; + + return; + } + + awcPromise = fetch(proxy(`https://awc.moe/challenger/${badger.name}`)); + }); + + onDestroy(() => { + if (browser) + Array.from(document.head.querySelectorAll('style')).forEach((style) => { + if (style.dataset.badgeWall) style.remove(); + }); + }); + + const submitBadge = () => { + const imageURL = document.querySelector('input[name="image_url"]') as HTMLInputElement; + const activityURL = document.querySelector('input[name="activity_url"]') as HTMLInputElement; + const description = document.querySelector('input[name="description"]') as HTMLInputElement; + const time = document.querySelector('input[type="datetime-local"]') as HTMLInputElement; + const category = document.querySelector('input[name="category"]') as HTMLInputElement; + const hidden = document.querySelector('input[name="hidden"]') as HTMLInputElement; + const source = document.querySelector('input[name="source"]') as HTMLInputElement; + const designer = document.querySelector('input[name="designer"]') as HTMLInputElement; + + if (!imageURL.value) { + error = 'Image URL cannot be empty.'; + + return; + } + + if ( + !imageURL.value.startsWith('http') || + (activityURL.value.length > 0 && !activityURL.value.startsWith('http')) + ) { + error = 'URLs must start with http or https.'; + + return; + } + + updateBadgeQuery + .mutate({ + id: selectedBadge?.id, + image: imageURL.value, + post: activityURL.value || '#', + description: description.value, + category: category.value, + time: time.valueAsDate ? inputTimeToDatabaseTime(time.valueAsDate) : undefined, + hidden: hidden.value === 'Hidden', + source: source.value, + designer: designer.value + }) + .then(() => { + error = null; + imageURL.value = ''; + activityURL.value = ''; + description.value = ''; + category.value = ''; + hidden.value = 'Shown'; + selectedBadge = undefined; + source.value = ''; + designer.value = ''; + }); + }; + + const removeAllBadges = () => { + if (confirmPrune === 2) { + confirmPrune = 0; + } else if (confirmPrune === 0) { + confirmPrune = 1; + + return; + } else { + confirmPrune = 2; + + return; + } + + selectedBadge = undefined; + + pruneBadgesQuery.mutate(null).then(); + }; + + const removeBadge = (badge: Badge) => { + if (!badge.id) return; + + if (confirmDelete === badge.id * 2) { + confirmDelete = 0; + } else if (confirmDelete / 4 === badge.id) { + confirmDelete = badge.id * 2; + + return; + } else { + confirmDelete = badge.id * 2; + + return; + } + + selectedBadge = undefined; + + deleteBadgeQuery + .mutate({ + id: badge.id + }) + .then(); + }; + + const groupBadges = (badges: IndexedBadge[]) => { + const groupedBadges: GroupedBadges = {}; + + badges.forEach((badge) => { + if (!badge.category) badge.category = 'Uncategorised'; + + if (!groupedBadges[badge.category]) groupedBadges[badge.category] = []; + + groupedBadges[badge.category].push(badge); + }); + + Object.entries(groupedBadges).forEach(([_category, badges]) => { + badges.forEach((badge, index) => { + badge.index = index; + }); + }); + + return Object.entries(groupedBadges) + .sort((a, b) => a[1].length - b[1].length) + .sort((a, b) => { + const pinnedCategories = + preferences && preferences.pinned_badge_wall_categories + ? preferences.pinned_badge_wall_categories + : ([] as string[]); + const aIndex = pinnedCategories.indexOf(a[0]); + const bIndex = pinnedCategories.indexOf(b[0]); + + if (aIndex === -1 && bIndex === -1) return 0; + if (aIndex === -1) return 1; + if (bIndex === -1) return -1; + + return aIndex - bIndex; + }) + .reduce((set: GroupedBadges, [key, value]) => { + set[key] = value; + + return set; + }, {}); + }; + + const parsePost = async () => { + if (importImages && importImages.length > 0) importImages = undefined; + + const link = (document.querySelector('#import_activity_url') as HTMLInputElement).value; + const type = link.replace(/.*\/(activity|thread)\/(\d+).*/, '$1'); + const id = link.replace(/.*\/(activity|thread)\/(\d+).*/, '$2'); + + if (type !== 'activity') return null; + + let text = await activityText(parseInt(id), importReplies); + + const images: ImportImage[] = []; + + if (importLinks) { + Array.from(new DOMParser().parseFromString(text, 'text/html').querySelectorAll('a')).forEach( + (a) => { + const anchor = a as HTMLAnchorElement; + + if (anchor.querySelector('img')) { + images.push({ + link: anchor.href, + image: (anchor.querySelector('img') as HTMLImageElement).src + }); + } + } + ); + + text = text.replace(/<a.*?>.*?<img.*?>.*?<\/a>/g, ''); + + Array.from( + new DOMParser().parseFromString(text, 'text/html').querySelectorAll('img') + ).forEach((img) => { + const image = img as HTMLImageElement; + + images.push({ + image: image.src + }); + }); + } else { + Array.from( + new DOMParser().parseFromString(text, 'text/html').querySelectorAll('img') + ).forEach((img) => { + const image = img as HTMLImageElement; + + images.push({ + image: image.src + }); + }); + } + + importImages = images; + }; + + const importBadges = () => + fetch( + `/api/badges?import=true ${importCategory.length > 0 ? `&category=${encodeURIComponent(importCategory)}` : ''} `, - { - method: 'PUT', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify( - importImages?.map((image) => ({ - image: image.image, - post: image.link || '#', - category: importCategory - })) - ) - } - ).then(() => { - importMode = false; - importImages = undefined; - }); - - const migrateCategory = () => { - fetch( - `/api/badges?migrate=true&original=${encodeURIComponent( - (document.querySelector('#migrate_original') as HTMLInputElement).value - )}&new=${encodeURIComponent( - (document.querySelector('#migrate_new') as HTMLInputElement).value - )}`, - { - method: 'PUT' - } - ).then(() => (migrateMode = false)); - }; - - const hideCategory = () => { - hideCategoryQuery - .mutate({ - category: (document.querySelector('#category_hide') as HTMLInputElement).value - }) - .then(() => (hideMode = false)); - }; - - const removeHiddenBadges = (isOwner: boolean, badges: IndexedBadge[]) => - isOwner || authorised ? badges : badges.filter((b) => !b.hidden && !b.shadow_hidden); - - const setAdjacentCursor = (badges: IndexedBadge[], direction: number) => { - const currentCategory = selectedBadge?.category || 'Uncategorised'; - const currentBadge = selectedBadge?.index; - const categoryBadges = groupBadges(badges)[currentCategory]; - - if (!currentCategory || currentBadge === undefined) return; - - let previousBadge = categoryBadges[currentBadge + direction]; - - while (previousBadge && (previousBadge.hidden || previousBadge.shadow_hidden)) - previousBadge = categoryBadges[previousBadge.index + direction]; - - if (previousBadge) selectedBadge = previousBadge; - }; - - const adjacentBadgeExists = ( - selectedBadge: IndexedBadge | undefined, - badges: IndexedBadge[], - direction: number - ) => { - const currentCategory = selectedBadge?.category || 'Uncategorised'; - const currentBadge = selectedBadge?.index; - const categoryBadges = groupBadges(badges)[currentCategory]; - - if (!currentCategory || currentBadge === undefined || !categoryBadges) return; - - let previousBadge = categoryBadges[currentBadge + direction]; - - while (previousBadge && (previousBadge.hidden || previousBadge.shadow_hidden)) - previousBadge = categoryBadges[previousBadge.index + direction]; - - return previousBadge; - }; - - const castAsStringArray = (array: any[]) => array as string[]; - - const castBadgesToIndexedBadges = (array: any[]) => array as IndexedBadge[]; - - const shadowHideBadge = () => { - if (!selectedBadge && !authorised) return; - - shadowHideBadgeQuery - .mutate({ - id: badger.id as number, - state: selectedBadge?.shadow_hidden as boolean - }) - .then(); - }; + { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify( + importImages?.map((image) => ({ + image: image.image, + post: image.link || '#', + category: importCategory + })) + ) + } + ).then(() => { + importMode = false; + importImages = undefined; + }); + + const migrateCategory = () => { + fetch( + `/api/badges?migrate=true&original=${encodeURIComponent( + (document.querySelector('#migrate_original') as HTMLInputElement).value + )}&new=${encodeURIComponent( + (document.querySelector('#migrate_new') as HTMLInputElement).value + )}`, + { + method: 'PUT' + } + ).then(() => (migrateMode = false)); + }; + + const hideCategory = () => { + hideCategoryQuery + .mutate({ + category: (document.querySelector('#category_hide') as HTMLInputElement).value + }) + .then(() => (hideMode = false)); + }; + + const removeHiddenBadges = (isOwner: boolean, badges: IndexedBadge[]) => + isOwner || authorised ? badges : badges.filter((b) => !b.hidden && !b.shadow_hidden); + + const setAdjacentCursor = (badges: IndexedBadge[], direction: number) => { + const currentCategory = selectedBadge?.category || 'Uncategorised'; + const currentBadge = selectedBadge?.index; + const categoryBadges = groupBadges(badges)[currentCategory]; + + if (!currentCategory || currentBadge === undefined) return; + + let previousBadge = categoryBadges[currentBadge + direction]; + + while (previousBadge && (previousBadge.hidden || previousBadge.shadow_hidden)) + previousBadge = categoryBadges[previousBadge.index + direction]; + + if (previousBadge) selectedBadge = previousBadge; + }; + + const adjacentBadgeExists = ( + selectedBadge: IndexedBadge | undefined, + badges: IndexedBadge[], + direction: number + ) => { + const currentCategory = selectedBadge?.category || 'Uncategorised'; + const currentBadge = selectedBadge?.index; + const categoryBadges = groupBadges(badges)[currentCategory]; + + if (!currentCategory || currentBadge === undefined || !categoryBadges) return; + + let previousBadge = categoryBadges[currentBadge + direction]; + + while (previousBadge && (previousBadge.hidden || previousBadge.shadow_hidden)) + previousBadge = categoryBadges[previousBadge.index + direction]; + + return previousBadge; + }; + + const castAsStringArray = (array: any[]) => array as string[]; + + const castBadgesToIndexedBadges = (array: any[]) => array as IndexedBadge[]; + + const shadowHideBadge = () => { + if (!selectedBadge && !authorised) return; + + shadowHideBadgeQuery + .mutate({ + id: badger.id as number, + state: selectedBadge?.shadow_hidden as boolean + }) + .then(); + }; </script> <HeadTitle route={`${data.username}'s Badge Wall`} path={`/user/${data.username}`} /> {#if loadError} - <Popup fullscreen locked> - {loadError} - </Popup> + <Popup fullscreen locked> + {loadError} + </Popup> {:else} - {@const isOwner = $identity && (isId ? $identity.id : $identity.name) === data.username} - - {#if $BadgeWallUser.fetching || !$BadgeWallUser.data} - <Message message="Loading badges ..." /> - - <Skeleton grid={true} count={100} width="150px" height="170px" /> - {:else} - {@const ungroupedBadges = castBadgesToIndexedBadges($BadgeWallUser.data.User.badges)} - {@const isBadgeSelected = - selectedBadge && - selectedBadge !== undefined && - selectedBadge.image && - selectedBadge.image !== undefined && - !editMode} - - <div id="badges"> - {#if preferences && !preferences.hide_awc_badges} - <AWC {awcPromise} {categoryFilter} {isOwner} {preferences} /> - {/if} - - {#if ungroupedBadges === null} - <Message message="Loading badges ..." /> - - <Skeleton grid={true} count={10} width="150px" height="170px" /> - {:else} - {@const groupedBadges = Object.entries( - groupBadges(removeHiddenBadges(isOwner, ungroupedBadges)) - )} - - {#if isOwner || authorised} - {@const shadowHiddenCount = ungroupedBadges.filter((badge) => badge.shadow_hidden).length} - {@const shadowHidden = shadowHiddenCount > 0} - - {#if shadowHidden} - <div class="card"> - <b>Notice:</b> The Badge Wall overseer system has detected badges containing - AI-generated material on your wall. {shadowHiddenCount} of your badges have been shadow - hidden. - <p /> - You may use the "Un-shadow Hide Badges" button to unhide these badges, from where you will - be required to use the hide feature to hide these badges from the public, while allowing - them to stay visible to you as the account holder. - </div> - {:else if !noticeDismissed} - <div class="card"> - <b>Notice:</b> AniList has begun purging outbound links which contain AI-generated - material, this includes Badge Wall. If you have collected badges with AI-generated - elements, kindly use the hide feature to hide these badges from the public, while - allowing them to stay visible to you as the account holder. - <p /> - Failure to comply with this request at your earliest convenience will result in the hiding - of all badges from your Badge Wall. - <p /> - <button - on:click={() => { - noticeDismissed = true; - - localStorage.setItem('badgeWallNoticeDismissed', 'true'); - }} - > - Dismiss - </button> - </div> - {/if} - - <p /> - - <div class="card"> - {#if authorised} - <button on:click={setShadowHide}>Shadow Hide Badges</button> - {/if} - - {#if isOwner && authorised} - <span style="margin: 0 0.625rem;">•</span> - {/if} - - {#if isOwner} - <button - on:click={() => { - selectedBadge = undefined; - editMode = !editMode; - }} - > - {editMode - ? $locale().user.badges.editMode.disable - : $locale().user.badges.editMode.enable} - </button> - <span style="margin: 0 0.625rem;">•</span> - <button - on:click={() => { - selectedBadge = undefined; - importMode = !importMode; - }} - > - {importMode - ? $locale().user.badges.importMode.disable - : $locale().user.badges.importMode.enable} - </button> - <span style="margin: 0 0.625rem;">•</span> - <button - on:click={() => { - selectedBadge = undefined; - migrateMode = !migrateMode; - }} - > - Migrate Category - </button> - <span style="margin: 0 0.625rem;">•</span> - <button - on:click={() => { - selectedBadge = undefined; - hideMode = !hideMode; - }} - > - Hide Category - </button> - <!-- <!-- <span style="margin: 0 0.625rem;">•</span> --> - <!-- <button on:click={() => exportBadges(groupedBadges)}>Export Badges</button> --> - - {#if shadowHidden} - <span style="margin: 0 0.625rem;">•</span> - <button on:click={setShadowHide}>Un-shadow Hide Badges</button> - {/if} - - {#if editMode && isOwner} - {@const groups = groupedBadges - .map((group) => group[0]) - .filter((group) => group !== 'Uncategorised')} - {@const designers = castAsStringArray([ - ...new Set( - ungroupedBadges - .map((badge) => badge.designer) - .filter((designer) => designer !== undefined && designer !== null) - .filter( - (designer, index, array) => - array.indexOf(designer) === index && !array.includes(`@${designer}`) - ) - ) - ])} - - <p /> - - {#if error} - <p style="color: red;">{error}</p> - {/if} - - <input - type="text" - placeholder={$locale().user.badges.editMode.imageURL} - name="image_url" - minlength="1" - maxlength="1000" - size="15" - value={selectedBadge ? selectedBadge.image : ''} - /> - <input - type="text" - placeholder={$locale().user.badges.editMode.activityURL} - name="activity_url" - minlength="1" - maxlength="1000" - size="15" - value={selectedBadge - ? selectedBadge.post === '#' - ? '' - : selectedBadge.post - : ''} - /> - <input - type="text" - placeholder={$locale().user.badges.editMode.description} - name="description" - minlength="1" - maxlength="1000" - size="15" - value={selectedBadge ? selectedBadge.description : ''} - /> - <Dropdown - items={groups.map((group) => ({ - name: group, - url: '#', - onClick: () => { - const category = document.querySelector('input[name="category"]'); - - if (category instanceof HTMLInputElement) category.value = group; - } - }))} - header={false} - center={false} - > - <span slot="title"> - <input - type="text" - placeholder={$locale().user.badges.editMode.category} - name="category" - minlength="1" - maxlength="1000" - size="15" - value={selectedBadge - ? selectedBadge.category === 'Uncategorised' - ? '' - : selectedBadge.category - : ''} - list="categories" - /> - </span> - </Dropdown> - <span style="float: right;"> - <input - type="datetime-local" - value={selectedBadge && selectedBadge.time - ? dateToInputTime(databaseTimeToDate(selectedBadge.time)) - : ''} - /> - <small>Must be full date and time, defaults to now if any fields empty</small> - </span> - - <p /> - - <div class="edit-row-2"> - <input - type="text" - placeholder={$locale().user.badges.editMode.source} - name="source" - minlength="1" - maxlength="1000" - size="16" - value={selectedBadge ? selectedBadge.source : ''} - /> - <Dropdown - items={designers.map((designer) => ({ - name: designer, - url: '#', - onClick: () => { - const designerField = document.querySelector('input[name="designer"]'); - - if (designerField instanceof HTMLInputElement) - designerField.value = designer; - } - }))} - header={false} - center={false} - > - <span slot="title"> - <input - type="text" - placeholder={$locale().user.badges.editMode.designer} - name="designer" - minlength="1" - maxlength="1000" - size="17" - value={selectedBadge ? selectedBadge.designer : ''} - /> - </span> - </Dropdown> - <Dropdown - items={[false, true].map((hidden) => ({ - name: hidden ? 'Hidden' : 'Shown', - url: '#', - onClick: () => { - const hiddenInput = document.querySelector('input[name="hidden"]'); - - if (hiddenInput instanceof HTMLInputElement) - hiddenInput.value = hidden ? 'Hidden' : 'Shown'; - } - }))} - header={false} - center={false} - > - <span slot="title"> - <input - type="text" - placeholder="Shown" - name="hidden" - minlength="1" - maxlength="1000" - size="15" - value={selectedBadge - ? selectedBadge.hidden - ? 'Hidden' - : 'Shown' - : 'Shown'} - /> - </span> - </Dropdown> - <button class="button-lined" on:click={submitBadge} - >{selectedBadge - ? $locale().user.badges.editMode.update - : $locale().user.badges.editMode.add}</button - > - {#if selectedBadge} - {$locale().user.badges.editMode.or} - <button - class="button-lined" - on:click={() => { - if (selectedBadge) removeBadge(selectedBadge); - }}>{$locale().user.badges.editMode.delete}</button - > - {/if} - </div> - {/if} - {/if} - </div> - {/if} - - <p /> - - <Badges - {ungroupedBadges} - {groupedBadges} - {categoryFilter} - {editMode} - {preferences} - bind:selectedBadge - /> - {/if} - </div> - - {#if isBadgeSelected} - <!-- {@const anyAdjacentBadgeExists = + {@const isOwner = $identity && (isId ? $identity.id : $identity.name) === data.username} + + {#if $BadgeWallUser.fetching || !$BadgeWallUser.data} + <Message message="Loading badges ..." /> + + <Skeleton grid={true} count={100} width="150px" height="170px" /> + {:else} + {@const ungroupedBadges = castBadgesToIndexedBadges($BadgeWallUser.data.User.badges)} + {@const isBadgeSelected = + selectedBadge && + selectedBadge !== undefined && + selectedBadge.image && + selectedBadge.image !== undefined && + !editMode} + + <div id="badges"> + {#if preferences && !preferences.hide_awc_badges} + <AWC {awcPromise} {categoryFilter} {isOwner} {preferences} /> + {/if} + + {#if ungroupedBadges === null} + <Message message="Loading badges ..." /> + + <Skeleton grid={true} count={10} width="150px" height="170px" /> + {:else} + {@const groupedBadges = Object.entries( + groupBadges(removeHiddenBadges(isOwner, ungroupedBadges)) + )} + + {#if isOwner || authorised} + {@const shadowHiddenCount = ungroupedBadges.filter((badge) => badge.shadow_hidden).length} + {@const shadowHidden = shadowHiddenCount > 0} + + {#if shadowHidden} + <div class="card"> + <b>Notice:</b> The Badge Wall overseer system has detected badges containing + AI-generated material on your wall. {shadowHiddenCount} of your badges have been shadow + hidden. + <p /> + You may use the "Un-shadow Hide Badges" button to unhide these badges, from where you will + be required to use the hide feature to hide these badges from the public, while allowing + them to stay visible to you as the account holder. + </div> + {:else if !noticeDismissed} + <div class="card"> + <b>Notice:</b> AniList has begun purging outbound links which contain AI-generated + material, this includes Badge Wall. If you have collected badges with AI-generated + elements, kindly use the hide feature to hide these badges from the public, while + allowing them to stay visible to you as the account holder. + <p /> + Failure to comply with this request at your earliest convenience will result in the hiding + of all badges from your Badge Wall. + <p /> + <button + on:click={() => { + noticeDismissed = true; + + localStorage.setItem('badgeWallNoticeDismissed', 'true'); + }} + > + Dismiss + </button> + </div> + {/if} + + <p /> + + <div class="card"> + {#if authorised} + <button on:click={setShadowHide}>Shadow Hide Badges</button> + {/if} + + {#if isOwner && authorised} + <span style="margin: 0 0.625rem;">•</span> + {/if} + + {#if isOwner} + <button + on:click={() => { + selectedBadge = undefined; + editMode = !editMode; + }} + > + {editMode + ? $locale().user.badges.editMode.disable + : $locale().user.badges.editMode.enable} + </button> + <span style="margin: 0 0.625rem;">•</span> + <button + on:click={() => { + selectedBadge = undefined; + importMode = !importMode; + }} + > + {importMode + ? $locale().user.badges.importMode.disable + : $locale().user.badges.importMode.enable} + </button> + <span style="margin: 0 0.625rem;">•</span> + <button + on:click={() => { + selectedBadge = undefined; + migrateMode = !migrateMode; + }} + > + Migrate Category + </button> + <span style="margin: 0 0.625rem;">•</span> + <button + on:click={() => { + selectedBadge = undefined; + hideMode = !hideMode; + }} + > + Hide Category + </button> + <!-- <!-- <span style="margin: 0 0.625rem;">•</span> --> + <!-- <button on:click={() => exportBadges(groupedBadges)}>Export Badges</button> --> + + {#if shadowHidden} + <span style="margin: 0 0.625rem;">•</span> + <button on:click={setShadowHide}>Un-shadow Hide Badges</button> + {/if} + + {#if editMode && isOwner} + {@const groups = groupedBadges + .map((group) => group[0]) + .filter((group) => group !== 'Uncategorised')} + {@const designers = castAsStringArray([ + ...new Set( + ungroupedBadges + .map((badge) => badge.designer) + .filter((designer) => designer !== undefined && designer !== null) + .filter( + (designer, index, array) => + array.indexOf(designer) === index && !array.includes(`@${designer}`) + ) + ) + ])} + + <p /> + + {#if error} + <p style="color: red;">{error}</p> + {/if} + + <input + type="text" + placeholder={$locale().user.badges.editMode.imageURL} + name="image_url" + minlength="1" + maxlength="1000" + size="15" + value={selectedBadge ? selectedBadge.image : ''} + /> + <input + type="text" + placeholder={$locale().user.badges.editMode.activityURL} + name="activity_url" + minlength="1" + maxlength="1000" + size="15" + value={selectedBadge + ? selectedBadge.post === '#' + ? '' + : selectedBadge.post + : ''} + /> + <input + type="text" + placeholder={$locale().user.badges.editMode.description} + name="description" + minlength="1" + maxlength="1000" + size="15" + value={selectedBadge ? selectedBadge.description : ''} + /> + <Dropdown + items={groups.map((group) => ({ + name: group, + url: '#', + onClick: () => { + const category = document.querySelector('input[name="category"]'); + + if (category instanceof HTMLInputElement) category.value = group; + } + }))} + header={false} + center={false} + > + <span slot="title"> + <input + type="text" + placeholder={$locale().user.badges.editMode.category} + name="category" + minlength="1" + maxlength="1000" + size="15" + value={selectedBadge + ? selectedBadge.category === 'Uncategorised' + ? '' + : selectedBadge.category + : ''} + list="categories" + /> + </span> + </Dropdown> + <span style="float: right;"> + <input + type="datetime-local" + value={selectedBadge && selectedBadge.time + ? dateToInputTime(databaseTimeToDate(selectedBadge.time)) + : ''} + /> + <small>Must be full date and time, defaults to now if any fields empty</small> + </span> + + <p /> + + <div class="edit-row-2"> + <input + type="text" + placeholder={$locale().user.badges.editMode.source} + name="source" + minlength="1" + maxlength="1000" + size="16" + value={selectedBadge ? selectedBadge.source : ''} + /> + <Dropdown + items={designers.map((designer) => ({ + name: designer, + url: '#', + onClick: () => { + const designerField = document.querySelector('input[name="designer"]'); + + if (designerField instanceof HTMLInputElement) + designerField.value = designer; + } + }))} + header={false} + center={false} + > + <span slot="title"> + <input + type="text" + placeholder={$locale().user.badges.editMode.designer} + name="designer" + minlength="1" + maxlength="1000" + size="17" + value={selectedBadge ? selectedBadge.designer : ''} + /> + </span> + </Dropdown> + <Dropdown + items={[false, true].map((hidden) => ({ + name: hidden ? 'Hidden' : 'Shown', + url: '#', + onClick: () => { + const hiddenInput = document.querySelector('input[name="hidden"]'); + + if (hiddenInput instanceof HTMLInputElement) + hiddenInput.value = hidden ? 'Hidden' : 'Shown'; + } + }))} + header={false} + center={false} + > + <span slot="title"> + <input + type="text" + placeholder="Shown" + name="hidden" + minlength="1" + maxlength="1000" + size="15" + value={selectedBadge + ? selectedBadge.hidden + ? 'Hidden' + : 'Shown' + : 'Shown'} + /> + </span> + </Dropdown> + <button class="button-lined" on:click={submitBadge} + >{selectedBadge + ? $locale().user.badges.editMode.update + : $locale().user.badges.editMode.add}</button + > + {#if selectedBadge} + {$locale().user.badges.editMode.or} + <button + class="button-lined" + on:click={() => { + if (selectedBadge) removeBadge(selectedBadge); + }}>{$locale().user.badges.editMode.delete}</button + > + {/if} + </div> + {/if} + {/if} + </div> + {/if} + + <p /> + + <Badges + {ungroupedBadges} + {groupedBadges} + {categoryFilter} + {editMode} + {preferences} + bind:selectedBadge + /> + {/if} + </div> + + {#if isBadgeSelected} + <!-- {@const anyAdjacentBadgeExists = adjacentBadgeExists(selectedBadge, ungroupedBadges, -1) || adjacentBadgeExists(selectedBadge, ungroupedBadges, 1)} --> - <Popup - fullscreen - show={isBadgeSelected} - onLeave={() => { - selectedBadge = undefined; - }} - > - <BadgePreview - bind:selectedBadge - onNext={() => setAdjacentCursor(ungroupedBadges, 1)} - onPrevious={() => setAdjacentCursor(ungroupedBadges, -1)} - hasNext={adjacentBadgeExists(selectedBadge, ungroupedBadges, 1) !== undefined} - hasPrevious={adjacentBadgeExists(selectedBadge, ungroupedBadges, -1) !== undefined} - /> - - {#if authorised} - <button on:click={shadowHideBadge}> - {#if selectedBadge && selectedBadge.shadow_hidden} - Un-shadow - {:else} - Shadow - {/if} Hide Badge ({selectedBadge ? selectedBadge.id : 0}) - </button> - {/if} - </Popup> - {/if} - {/if} + <Popup + fullscreen + show={isBadgeSelected} + onLeave={() => { + selectedBadge = undefined; + }} + > + <BadgePreview + bind:selectedBadge + onNext={() => setAdjacentCursor(ungroupedBadges, 1)} + onPrevious={() => setAdjacentCursor(ungroupedBadges, -1)} + hasNext={adjacentBadgeExists(selectedBadge, ungroupedBadges, 1) !== undefined} + hasPrevious={adjacentBadgeExists(selectedBadge, ungroupedBadges, -1) !== undefined} + /> + + {#if authorised} + <button on:click={shadowHideBadge}> + {#if selectedBadge && selectedBadge.shadow_hidden} + Un-shadow + {:else} + Shadow + {/if} Hide Badge ({selectedBadge ? selectedBadge.id : 0}) + </button> + {/if} + </Popup> + {/if} + {/if} {/if} {#if true} - <Popup fullscreen onLeave={() => (importMode = false)} show={importMode}> - {$locale().user.badges.importMode.title} - - <p /> - - <input - type="text" - placeholder={$locale().user.badges.editMode.activityURL} - id="import_activity_url" - minlength="1" - maxlength="1000" - size="20" - /> - <input - type="text" - placeholder={$locale().user.badges.editMode.category} - id="import_category" - minlength="1" - maxlength="1000" - size="20" - /> - - <p /> - - <input type="checkbox" id="import_links" name="import_links" bind:checked={importLinks} /> - {$locale().user.badges.importMode.importLinks.title} - <SettingHint lineBreak> - {$locale().user.badges.importMode.importLinks.hint} - </SettingHint> - - <p /> - - <input type="checkbox" id="import_links" name="import_links" bind:checked={importReplies} /> - {$locale().user.badges.importMode.importReplies} - - <p /> - - <button - on:click={() => { - importMode = false; - importImages = undefined; - }} - class="button-lined" - > - {$locale().user.badges.importMode.cancel} - </button> - <button on:click={() => parsePost()} class="button-lined" style="float: right;"> - {$locale().user.badges.importMode.fetch} - </button> - - <p /> - - <details> - <summary>{$locale().user.badges.importMode.dangerous}</summary> - - <button - class="button-lined no-shadow" - data-umami-event="Remove All Badges" - on:click={removeAllBadges} - > - {$locale({ - values: { - times: 3 - confirmPrune - } - }).user.badges.importMode.deleteAll.title} - </button> - <SettingHint lineBreak> - {$locale().user.badges.importMode.deleteAll.hint} - </SettingHint> - </details> - - {#if importImages && importImages.length > 0} - <p /> - - {$locale({ - values: { - count: importImages.length - } - }).user.badges.importMode.importConfirm} - <button - on:click={() => importBadges()} - class="button-lined no-shadow" - data-umami-event="Import Badges" - > - {$locale().user.badges.importMode.import} - </button> - - <SettingHint lineBreak> - {$locale().user.badges.importMode.importWait} - </SettingHint> - {/if} - </Popup> + <Popup fullscreen onLeave={() => (importMode = false)} show={importMode}> + {$locale().user.badges.importMode.title} + + <p /> + + <input + type="text" + placeholder={$locale().user.badges.editMode.activityURL} + id="import_activity_url" + minlength="1" + maxlength="1000" + size="20" + /> + <input + type="text" + placeholder={$locale().user.badges.editMode.category} + id="import_category" + minlength="1" + maxlength="1000" + size="20" + /> + + <p /> + + <input type="checkbox" id="import_links" name="import_links" bind:checked={importLinks} /> + {$locale().user.badges.importMode.importLinks.title} + <SettingHint lineBreak> + {$locale().user.badges.importMode.importLinks.hint} + </SettingHint> + + <p /> + + <input type="checkbox" id="import_links" name="import_links" bind:checked={importReplies} /> + {$locale().user.badges.importMode.importReplies} + + <p /> + + <button + on:click={() => { + importMode = false; + importImages = undefined; + }} + class="button-lined" + > + {$locale().user.badges.importMode.cancel} + </button> + <button on:click={() => parsePost()} class="button-lined" style="float: right;"> + {$locale().user.badges.importMode.fetch} + </button> + + <p /> + + <details> + <summary>{$locale().user.badges.importMode.dangerous}</summary> + + <button + class="button-lined no-shadow" + data-umami-event="Remove All Badges" + on:click={removeAllBadges} + > + {$locale({ + values: { + times: 3 - confirmPrune + } + }).user.badges.importMode.deleteAll.title} + </button> + <SettingHint lineBreak> + {$locale().user.badges.importMode.deleteAll.hint} + </SettingHint> + </details> + + {#if importImages && importImages.length > 0} + <p /> + + {$locale({ + values: { + count: importImages.length + } + }).user.badges.importMode.importConfirm} + <button + on:click={() => importBadges()} + class="button-lined no-shadow" + data-umami-event="Import Badges" + > + {$locale().user.badges.importMode.import} + </button> + + <SettingHint lineBreak> + {$locale().user.badges.importMode.importWait} + </SettingHint> + {/if} + </Popup> {/if} <Popup fullscreen onLeave={() => (migrateMode = false)} show={migrateMode}> - Migrate Category - - <p /> - - <input - type="text" - placeholder="Original Category" - id="migrate_original" - minlength="1" - maxlength="1000" - size="20" - /> - <input - type="text" - placeholder="New Category" - id="migrate_new" - minlength="1" - maxlength="1000" - size="20" - /> - <SettingHint lineBreak>Leave category empty to migrate all to or from uncategorised.</SettingHint> - - <p /> - - <button - on:click={() => { - importMode = false; - importImages = undefined; - }} - class="button-lined" - > - {$locale().user.badges.importMode.cancel} - </button> - <button on:click={() => migrateCategory()} class="button-lined" style="float: right;"> - Migrate - </button> + Migrate Category + + <p /> + + <input + type="text" + placeholder="Original Category" + id="migrate_original" + minlength="1" + maxlength="1000" + size="20" + /> + <input + type="text" + placeholder="New Category" + id="migrate_new" + minlength="1" + maxlength="1000" + size="20" + /> + <SettingHint lineBreak>Leave category empty to migrate all to or from uncategorised.</SettingHint> + + <p /> + + <button + on:click={() => { + importMode = false; + importImages = undefined; + }} + class="button-lined" + > + {$locale().user.badges.importMode.cancel} + </button> + <button on:click={() => migrateCategory()} class="button-lined" style="float: right;"> + Migrate + </button> </Popup> <Popup fullscreen onLeave={() => (hideMode = false)} show={hideMode}> - Hide Category - - <SettingHint lineBreak> - If the majority of the badges in a category are shown, the category will be hidden, and vice - versa. - </SettingHint> - - <p /> - - <input - type="text" - placeholder="Category" - id="category_hide" - minlength="1" - maxlength="1000" - size="20" - /> - <SettingHint lineBreak>Leave category field empty to hide all.</SettingHint> - - <p /> - - <button - on:click={() => { - hideMode = false; - importImages = undefined; - }} - class="button-lined" - > - {$locale().user.badges.importMode.cancel} - </button> - <button on:click={() => hideCategory()} class="button-lined" style="float: right;" - >Toggle Visibility</button - > + Hide Category + + <SettingHint lineBreak> + If the majority of the badges in a category are shown, the category will be hidden, and vice + versa. + </SettingHint> + + <p /> + + <input + type="text" + placeholder="Category" + id="category_hide" + minlength="1" + maxlength="1000" + size="20" + /> + <SettingHint lineBreak>Leave category field empty to hide all.</SettingHint> + + <p /> + + <button + on:click={() => { + hideMode = false; + importImages = undefined; + }} + class="button-lined" + > + {$locale().user.badges.importMode.cancel} + </button> + <button on:click={() => hideCategory()} class="button-lined" style="float: right;" + >Toggle Visibility</button + > </Popup> |